﻿namespace Bio.Structure
{
    /// <summary>
    /// Describes the bonds between atoms in a protein structure.
    /// </summary>
    public class Bond
    {
        #region Public Properties

        /// <summary>
        /// Gets the order of the bond.
        /// </summary>
        public int BondOrder { get; private set; }

        /// <summary>
        /// Gets the type of bond such as Covalent, Ionic, etc.
        /// </summary>
        public BondType BondType { get; private set; }

        /// <summary>
        /// Gets the first atom of the bond.
        /// </summary>
        public IAtom FirstAtom { get; private set; }

        /// <summary>
        /// Gets the second atom of the bond.
        /// </summary>
        public IAtom SecondAtom { get; private set; }

        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="Bond"/> class. 
        /// A constructor for the Bond type
        /// </summary>
        /// <param name="firstAtom">
        /// First atom in the bond
        /// </param>
        /// <param name="secondAtom">
        /// Second atom in the bond
        /// </param>
        /// <param name="bondType">
        /// Bond type
        /// </param>
        /// <param name="bondOrder">
        /// Bond order
        /// </param>
        public Bond(IAtom firstAtom, IAtom secondAtom, BondType bondType, int bondOrder)
        {
            this.BondType = bondType;
            this.FirstAtom = firstAtom;
            this.SecondAtom = secondAtom;
            this.BondOrder = bondOrder;
        }
        #endregion

        #region Public Methods and Operators

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
        ///     <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified object  is equal to the current object; otherwise, false.
        /// </returns>
        /// <param name="obj">
        /// The object to compare with the current object. 
        /// </param>
        public override bool Equals(object obj)
        {
            return !ReferenceEquals(null, obj) && obj.GetType() == this.GetType()
                   && (ReferenceEquals(this, obj) || this.Equals((Bond)obj));
        }

        /// <summary>
        ///     Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        ///     A hash code for the current <see cref="T:System.Object" />.
        /// </returns>
        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = (int)this.BondType;
                hashCode = (hashCode * 397) ^ (this.FirstAtom != null ? this.FirstAtom.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ (this.SecondAtom != null ? this.SecondAtom.GetHashCode() : 0);
                hashCode = (hashCode * 397) ^ this.BondOrder;
                return hashCode;
            }
        }

        /// <summary>
        ///     Returns a string that represents the current object.
        /// </summary>
        /// <returns>
        ///     A string that represents the current object.
        /// </returns>
        public override string ToString()
        {
            return string.Format(
                "{0} FirstAtom:{1}, SecondAtom:{2}, Order:{3}", 
                this.BondType, 
                this.FirstAtom, 
                this.SecondAtom, 
                this.BondOrder);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
        ///     <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// true if the specified object  is equal to the current object; otherwise, false.
        /// </returns>
        /// <param name="other">
        /// The object to compare with the current object. 
        /// </param>
        protected bool Equals(Bond other)
        {
            return this.BondType == other.BondType 
                && object.Equals(this.FirstAtom, other.FirstAtom)
                && object.Equals(this.SecondAtom, other.SecondAtom) 
                && this.BondOrder == other.BondOrder;
        }

        #endregion
    }
}